home *** CD-ROM | disk | FTP | other *** search
- /* mv_dir -- rename directory
- Copyright (C) 1990 Free Software Foundation, Inc.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 1, or (at your option)
- any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
-
- /* Helper program for GNU mv on machines that lack the rename system call.
-
- Usage: mv_dir from to
-
- FROM must be an existing directory. TO must not exist.
-
- Must be setuid root.
-
- Ian Dall (ian@sibyl.eleceng.ua.oz.au)
- and David MacKenzie (djm@ai.mit.edu) */
-
- #include <stdio.h>
- #include <errno.h>
- #include <sys/types.h>
- #include <signal.h>
- #include "system.h"
-
- #ifdef STDC_HEADERS
- #include <stdlib.h>
- #include <errno.h>
- #else
- char *malloc ();
-
- extern int errno;
- #endif
-
- #ifndef HIPRI
- #define HIPRI -10
- #endif
-
- #ifdef DEBUG
- #define link(FROM, TO) (printf("Linking %s to %s\n", FROM, TO), 0)
- #define unlink(FILE) (printf("Unlinking %s\n", FILE), 0)
- #endif
-
- /* The name this program was run with. */
- char *program_name;
-
- char *basename ();
- char *xmalloc ();
- void error ();
- void strip_trailing_slashes ();
-
- /* Return the name of the directory containing PATH. */
-
- char *
- parent_dir (path)
- char *path;
- {
- char *dir;
- char *base;
- int length;
-
- base = rindex (path, '/');
- if (base == NULL)
- return ".";
-
- if (base > path)
- base--;
- length = base - path + 1;
- dir = xmalloc (length + 1);
- strncpy (dir, path, length);
- dir[length] = '\0';
- return dir;
- }
-
- void
- main (argc, argv)
- int argc;
- char **argv;
- {
- char *from, *to, *from_base, *from_parent, *to_parent;
- struct stat from_stats, to_stats;
- char *next_slash, temp;
- int i;
-
- program_name = argv[0];
- if (argc != 3)
- {
- fprintf (stderr, "Usage: %s existing-dir new-dir\n", program_name);
- exit (2);
- }
- from = argv[1];
- to = argv[2];
- strip_trailing_slashes (from);
- strip_trailing_slashes (to);
- from_parent = parent_dir (from);
- to_parent = parent_dir (to);
-
- /* Make sure `from' is not "." or "..". */
- from_base = basename (from);
- if (!strcmp (from_base, ".") || !strcmp (from_base, ".."))
- error (1, 0, "cannot rename `.' or `..'");
-
- /* Even with an effective uid of root, link fails if the target exists.
- That is what we want, so don't unlink `to' first.
- However, we do need to check that the directories that link and unlink
- will modify are writable by the user. */
-
- if (stat (from, &from_stats))
- error (1, errno, "%s", from);
- if ((from_stats.st_mode & S_IFMT) != S_IFDIR)
- error (1, 0, "`%s' is not a directory", from);
- if (access (from_parent, W_OK))
- error (1, errno, "cannot write to `%s'", from_parent);
- if (access (to_parent, W_OK))
- error (1, errno, "cannot write to `%s'", to_parent);
-
- /* We can't make this atomic, but we do our best. */
- for (i = NSIG; i > 0; i--)
- if (i != SIGKILL)
- signal (i, SIG_IGN);
- setuid (0); /* Make real uid 0 so it is harder to kill. */
- nice (HIPRI - nice (0)); /* Raise priority. */
- /* Make sure that `from' is not an ancestor of `to', to prevent
- corruption of the file system in cases like
- mv_dir foo foo/bar/baz
- where foo and foo/bar are directories and foo/bar/baz does not exist. */
-
- next_slash = to;
- while ((next_slash = index (next_slash, '/')) != NULL)
- {
- temp = *++next_slash;
- *next_slash = '\0';
- if (stat (to, &to_stats))
- error (1, errno, "%s", to);
- *next_slash = temp;
-
- if (to_stats.st_dev == from_stats.st_dev
- && to_stats.st_ino == from_stats.st_ino)
- error (1, 0, "`%s' is an ancestor of `%s'", from, to);
- }
-
- if (link (from, to))
- error (1, errno, "cannot link `%s' to `%s'", from, to);
- if (unlink (from))
- error (1, errno, "cannot unlink `%s'", from);
-
- /* Replace the directory's `..' entry. It used to be a link to
- the parent of `from'; make it a link to the parent of `to' instead. */
- i = strlen (to);
- next_slash = xmalloc (i + 4);
- strcpy (next_slash, to);
- strcpy (next_slash + i, "/..");
- if (unlink (next_slash) && errno != ENOENT)
- error (1, errno, "cannot unlink `%s'", next_slash);
- if (link (to_parent, next_slash))
- error (1, errno, "cannot link `%s' to `%s'", to_parent, next_slash);
-
- exit (0);
- }
-
- /* Return NAME with any leading path stripped off. */
-
- char *
- basename (name)
- char *name;
- {
- char *base;
-
- base = rindex (name, '/');
- return base ? base + 1 : name;
- }
-
- /* Allocate `n' bytes of memory dynamically, with error checking. */
-
- char *
- xmalloc (n)
- unsigned n;
- {
- char *p;
-
- p = malloc (n);
- if (p == 0)
- error (1, 0, "virtual memory exhausted");
- return p;
- }
-
- /* Remove trailing slashes from STR;
- they cause some system calls to fail. */
-
- void
- strip_trailing_slashes (str)
- char *str;
- {
- int last = strlen (str) - 1;
-
- while (last > 0 && str[last] == '/')
- str[last--] = '\0';
- }
-